﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Principal;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Net.Http;
using System.Web.Routing;

using ERPStore.Models;
using ERPStore.Services;
using ERPStore.Tests.Controllers;
using ERPStore.Web.Extensions;

using Microsoft.Practices.Unity;

using Moq;

using NUnit.Framework;

using ERPStore.Extensions;

namespace ERPStore.Tests
{
	/// <summary>
	/// Summary description for UnitTest1
	/// </summary>
	[TestFixture]
	public abstract class TestBase
	{
		private Mock<HttpContextBase> m_MockHttpContext;
		protected string m_FolderView;

		protected ERPStore.Logging.ILogger Logger { get; private set; }

		public virtual void Initialize()
		{
			var httpContext = GetHttpContext();

			ERPStore.Web.ERPStoreStartup.Register(httpContext, ExecutingContext.Test);
			GlobalConfiguration.Configuration.DebugEnabled = true;

			var container = GlobalConfiguration.Configuration.DependencyResolver.GetContainer();

			// Override Services
			container.RegisterType<ERPStore.Logging.ILogger, ERPStore.Logging.DiagnosticsLogger>();
			container.RegisterType<EventBrokR.IPublisher, SyncEventPublisher>(new ContainerControlledLifetimeManager());
			this.Logger = container.Resolve<ERPStore.Logging.ILogger>();
		}

		protected void TeardownHttpContext()
		{
			m_MockHttpContext = null;
		}

		public HttpContextBase GetHttpContext()
		{
			if (m_MockHttpContext == null)
			{
				m_MockHttpContext = CreateMockHttpContext();
			}
			return m_MockHttpContext.Object;
		}

		protected T CreateController<T>()
			where T : System.Web.Mvc.Controller
		{
			var httpContext = GetHttpContext();
			var result = CreateController<T>(httpContext);
			return result;
		}

		protected T CreateController<T>(System.Web.HttpContextBase httpContext)
			where T : System.Web.Mvc.Controller
		{
			// m_Container.RegisterInstance(typeof(System.Web.HttpContextBase), httpContext, new PerThreadLifetimeManager());
			var controller = GlobalConfiguration.Configuration.DependencyResolver.GetService<T>();
			controller.SetupControllerContext(httpContext);

			return controller;
		}

		public T CreateAuthenticatedController<T>()
			where T : System.Web.Mvc.Controller
		{
			var accountService = GlobalConfiguration.Configuration.DependencyResolver.GetService<IAccountService>();
			var user = TestHelper.GetDefaultConnectedUser();
			var principal = user.GetCustomerUserPrincipal();
			return CreateAuthenticatedController<T>(principal);
		}

		public T CreateAuthenticatedController<T>(ERPStore.Models.UserPrincipal authenticatedPrincipal)
			where T : System.Web.Mvc.Controller
		{
			var httpContext = CreateMockHttpContext();
			var container = GlobalConfiguration.Configuration.DependencyResolver.GetContainer();
			container.RegisterInstance(typeof(System.Web.HttpContextBase), httpContext.Object, new PerThreadLifetimeManager());

			httpContext.Setup(ctx => ctx.User).Returns(authenticatedPrincipal);

			var controller = GlobalConfiguration.Configuration.DependencyResolver.GetService<T>();
			controller.SetupControllerContext(httpContext.Object);

			// Ajout du cookie authentifié
			httpContext.Object.Response.AddAuthenticatedCookie(authenticatedPrincipal.CurrentUser.Id, true);

			return controller;
		}

		public T CreateControllerWithForm<T>(FormCollection form)
			where T : System.Web.Mvc.Controller
		{
			var httpContext = CreateMockHttpContext();
			var mock = Moq.Mock.Get(httpContext.Object.Request);
			mock.Setup(i => i.Form).Returns(form);
			var container = GlobalConfiguration.Configuration.DependencyResolver.GetContainer();
			container.RegisterInstance(typeof(System.Web.HttpContextBase), httpContext.Object, new PerThreadLifetimeManager());
			var controller = container.Resolve<T>();
			controller.SetupControllerContext(httpContext.Object);

			return controller;
			
		}

		public T CreateApiController<T>()
			where T : System.Web.Http.ApiController
		{
			var controller = GlobalConfiguration.Configuration.DependencyResolver.GetService<T>();
			controller.Request = new HttpRequestMessage();
			controller.Request.Headers.Add("apikey", "test");
			controller.ControllerContext = new System.Web.Http.Controllers.HttpControllerContext();
			return controller;
		}

		private Mock<HttpContextBase> CreateMockHttpContext()
		{
			var context = new Mock<HttpContextBase>();
			var cookies = new HttpCookieCollection();

			// Response
			var response = new Mock<HttpResponseBase>();
			var cachePolicy = new Mock<HttpCachePolicyBase>();
			response.SetupProperty(r => r.StatusCode, 200);
			response.Setup(r => r.Cache).Returns(cachePolicy.Object);
			response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(r => r);
			response.Setup(r => r.Cookies).Returns(cookies);
			context.Setup(ctx => ctx.Response).Returns(response.Object);

			// Request
			var request = new Mock<HttpRequestBase>();
			var visitorId = Guid.NewGuid().ToString();
			var principal = new ERPStore.Models.UserPrincipal(visitorId);
			request.Setup(r => r.AnonymousID).Returns(principal.VisitorId);
			request.Setup(r => r.Cookies).Returns(cookies);
			request.Setup(r => r.Url).Returns(new Uri("http://www.test.com"));
			request.Setup(r => r.Headers).Returns(new System.Collections.Specialized.NameValueCollection());
			request.Setup(r => r.RequestContext).Returns(new System.Web.Routing.RequestContext(context.Object, new System.Web.Routing.RouteData()));
			request.SetupGet(x => x.PhysicalApplicationPath).Returns("/");
			request.Setup(r => r.UserHostAddress).Returns("127.0.0.1");
			request.Object.Cookies.Add(new HttpCookie("erpstorevid")
			{
				Value = principal.VisitorId,
			});
			request.SetupGet(r => r.QueryString).Returns(new System.Collections.Specialized.NameValueCollection());
			request.SetupGet(r => r.Form).Returns(new System.Collections.Specialized.NameValueCollection());
			request.SetupGet(r => r.PathInfo).Returns(string.Empty);
			request.SetupGet(r => r.AppRelativeCurrentExecutionFilePath).Returns("~/");
			context.Setup(ctx => ctx.Request).Returns(request.Object);

			// Sessions
			var session = new Mock<HttpSessionStateBase>();
			context.Setup(ctx => ctx.Session).Returns(session.Object);

			// Server
			var server = new Mock<HttpServerUtilityBase>();
			server.Setup(s => s.MapPath(It.IsAny<string>())).Returns<string>(r => {
				if (r.Equals("/bin", StringComparison.InvariantCultureIgnoreCase))
				{
					r = string.Empty;
				}
				var path = typeof(ERPStore.Web.MvcApplication).Assembly.Location;
				var fileName = System.IO.Path.GetFileName(path);
				path = path.Replace(fileName, string.Empty);
				r = r.Trim('/').Trim('\\');
				path = System.IO.Path.Combine(path, r);
				return path;
			});
			context.Setup(ctx => ctx.Server).Returns(server.Object);

			// Principal
			context.Setup(ctx => ctx.User).Returns(principal);

			// Items
			context.Setup(ctx => ctx.Items).Returns(new Dictionary<string, object>());

			return context;
		}

		protected TestControllerActionInvoker<Result> ControllerActionInvoker<Result>()
			where Result : ActionResult
		{
			var container = GlobalConfiguration.Configuration.DependencyResolver.GetContainer();
			return new TestControllerActionInvoker<Result>(container);
		}


		protected bool IsOnlyPostAllowed<T>(Expression<Action<T>> action)
		{
			var body = action.Body as MethodCallExpression;

			var attribute = body.Method.GetCustomAttributes(typeof(AcceptVerbsAttribute), false)
									 .Cast<AcceptVerbsAttribute>()
									 .SingleOrDefault();

			return (attribute != null && attribute.Verbs.Contains(HttpVerbs.Post.ToString().ToUpper()));
		}

		protected void AuthenticateRequest(System.Web.HttpContextBase ctx)
		{
			var visitorId = ((ERPStore.Models.UserPrincipal) ctx.User).VisitorId;
			var authCookie = ctx.Request.Cookies[FormsAuthentication.FormsCookieName];
			if (authCookie == null)
			{
				return;
			}

			var authenticationService = GlobalConfiguration.Configuration.DependencyResolver.GetService<ERPStore.Services.IAccountService>();

			Logger.Debug("RequestAuthenticated {0}", ctx.Request.Url);
			// Extraction et decryptage du ticket d'autentification
			FormsAuthenticationTicket authTicket = null;
			try
			{
				authTicket = FormsAuthentication.Decrypt(authCookie.Value);
			}
			catch (Exception ex)
			{
				Logger.Error(ex);
			}

			if (authTicket == null)
			{
				return;
			}

			// Recuperation du userId
			int userId = 0;

			int.TryParse(authTicket.Name, out userId);

			if (userId > 0)
			{
				// Creation d'une nouvelle identité
				var id = new FormsIdentity(authTicket);

				// On passe l'identité et les roles a l'objet Principal
				var principal = new Models.UserPrincipal(id, visitorId);
				var user = authenticationService.GetUserById(userId);
				if (user != null)
				{
					if (!user.LastLoginDate.HasValue)
					{
						user.LastLoginDate = DateTime.UtcNow;
					}
					principal.CurrentUser = user;
					Logger.Debug("User : {0} Logged", principal.CurrentUser.FullName);
				}

				var httpContext = GetHttpContext();
				var mock = Moq.Mock.Get(httpContext);
				mock.Setup(c => c.User).Returns(principal);
			}
		}

		public UrlHelper GetUrlHelper(string appPath = "/", RouteCollection routes = null)
		{
			if (routes == null)
			{
				routes = new RouteCollection();
			}

			var httpContext = new StubHttpContextForRouting(appPath);
			var routeData = new RouteData();
			routeData.Values.Add("controller", "defaultcontroller");
			routeData.Values.Add("action", "defaultaction");
			var requestContext = new RequestContext(httpContext, routeData);
			var helper = new UrlHelper(requestContext, routes);
			return helper;
		}

		public RouteData GetRouteDataByUrl(string url)
		{
			var ctx = new ERPStore.Tests.StubHttpContextForRouting(requestUrl: url);
			var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(ctx);
			return routeData;

		}

	}
}
